package skeletalAnimation
{
	import flash.geom.Vector3D;
	import flash.geom.Matrix3D;
	import flash.display.Sprite;
	
	public class SkeletalAnimation extends Sprite
	{
		public var character:Sprite;
		
		public const HEAD:int = 0;
		public const SHOULDER:int = 1;
		public const ELLBOW:int = 2;
		public const HAND:int = 3;
		public const HIP:int = 4;
		public const KNEE:int = 5;
		public const HEEL:int = 6;
		public const TOE:int = 7;
		public const WAIST:int = 8;

		public var walk:Array=[
			[[-3,-212,-11],[-1,-206,-14],[2,-196,-16],[5,-195,-16],[6,-203,-15],[4,-208,-14],[1,-204,-15],[-1,-196,-16],[-4,-195,-14],[-6,-208,-14]]
			,[[-49,-161,-16],[-47,-153,-16],[-43,-144,-19],[-39,-148,-20],[-40,-152,-23],[-38,-157,-22],[-42,-151,-23],[-45,-143,-23],[-48,-139,-23],[-51,-153,-16]]
			,[[-58,-85,0],[-61,-78,11],[-59,-74,3],[-57,-75,-5],[-57,-81,-13],[-57,-84,-22],[-55,-79,-31],[-57,-62,-33],[-57,-67,-26],[-59,-73,-14]]
			,[[-67,-11,54],[-55,-22,76],[-46,-20,74],[-44,-15,62],[-57,-9,39],[-69,1,6],[-72,7,-19],[-70,20,-30],[-70,23,-24],[-72,6,12]]
			,[[-31,-7,-8],[-27,-3,-12],[-27,-3,-10],[-23,0,-13],[-22,-9,-9],[-20,-15,0],[-21,-10,0],[-22,-6,-3],[-25,-7,4],[-28,-11,-3]]
			,[[-21,81,-6],[-19,93,-45],[-17,92,-55],[-22,89,-44],[-32,89,4],[-24,80,29],[-22,80,36],[-21,93,52],[-29,82,57],[-26,85,31]]
			,[[-13,206,-49],[-10,198,-94],[-12,184,-140],[-11,161,-156],[-13,155,-112],[-14,192,-33],[-12,208,54],[-13,207,87],[-16,209,57],[-12,209,6]]
			,[[-23,212,4],[-18,216,-56],[-16,215,-106],[-16,207,-141],[-17,198,-105],[-20,216,8],[-31,202,104],[-37,195,135],[-31,210,109],[-27,212,54]]
			,[[-26.66,-56,-8],[-24.66,-52,-9.33],[-23.33,-49,-9.66],[-20.66,-49.33,-11],[-20.66,-53.66,-10.66],[-19.33,-57.33,-7.33],[-21,-53.66,-7.66],[-22.33,-49.66,-8.66],[-24.33,-48.66,-6.33],[-26.33,-54.66,-6.33]]
			];
		public var walkDeltaFrame:Number = .1;
		public var walkLoopTime:Number = 3;
		
		//connecting lines
		private var lines:Array=[];
		
		public var actionPointsNumber:Number;
		public var actionFrameNumber:Number;		
		public var actionArray:Array;
		public var actionLoopTime:Number;
		
		//user input: mouse movement
		public var rotY:Number=Math.PI*0.75;//start with a rotated view
		public var rotX:Number=0;//Math.PI;//start with a rotated view
		
		public var currentFrame:Number=0;		
		
		public function SkeletalAnimation()
		{
			character = addChild( new  Sprite()) as Sprite;
			updateMatrix();
			
			setupAction();
			setupLines();
		}
		
		private function setupLines():void
		{
			var delta:int = actionPointsNumber;
		
			lines =
			[ 
			[SHOULDER,ELLBOW],[ELLBOW,HAND],[HIP,KNEE],[KNEE,HEEL],[HEEL,TOE]
			,[SHOULDER+delta,ELLBOW+delta],[ELLBOW+delta,HAND+delta],[HIP+delta,KNEE+delta],[KNEE+delta,HEEL+delta],[HEEL+delta,TOE+delta]
			,[SHOULDER,SHOULDER+delta]
			,[SHOULDER,WAIST],[WAIST,HIP],[SHOULDER+delta,WAIST+delta],[WAIST+delta,HIP+delta]
			,[HIP,HIP+delta]
			];			
		}
		private function updateMatrix():void
		{
			for(var i:int=0; i<walk.length-1; i++)
			{
				var str:String = "";
				str += "[";
				for(var j:int=0; j<walk[i].length; j++)
				{
					if(i==0)
					{
						//walk[i][j][2] -= 15;
						
						str += "[";
							str+=walk[i][j][0]+","+walk[i][j][1]+","+walk[i][j][2];
						str += "],";
					}
					if(i>4)
					{
						//walk[i][j][0] *= .5;
						//walk[i][j][1] *= 2;
						//walk[i][j][2] *= 2;
					}
				}
				str += "]";
			}
		}
		private function setupAction():void
		{
			var n:int;
			var frm:*;
			
			actionArray = walk;
			actionLoopTime = walkLoopTime;
			actionPointsNumber = actionArray.length;
			actionFrameNumber = actionArray[0].length;
			
			var shift:int = actionFrameNumber/2;
			
			for(var pts:int=0;pts<actionPointsNumber;pts++)
			{
				n=actionArray.length;
				actionArray[n]=new Array();
				
				for(frm in actionArray[pts])
				{
					actionArray[n][frm]=new Array();
					actionArray[n][frm][0]=-actionArray[pts][(frm+shift)%10][0];
					actionArray[n][frm][1]=actionArray[pts][(frm+shift)%10][1];
					actionArray[n][frm][2]=actionArray[pts][(frm+shift)%10][2];
				}
			}
		}
		public function update(time:Number):void
		{
			render(currentFrame);
			
			currentFrame+=(actionArray.length*time/actionLoopTime);
			
			currentFrame = int(currentFrame*100)/100;
			
			if(currentFrame>actionFrameNumber)
			{
				currentFrame-=actionFrameNumber;
			}
		}
		//render one frame (where frame can be a real)
		private function render(frame:Number):void
		{
			var pts:*;
			var ln:*;
			character.graphics.clear();
			character.graphics.beginFill(0x000000,0);
			character.graphics.lineStyle(1,0x000000);
			
			var m:Matrix3D = new Matrix3D();
				m.appendRotation(rotY * 180 / Math.PI, Vector3D.Y_AXIS);
				m.appendRotation(rotX * 180 / Math.PI, Vector3D.X_AXIS);
				
			var current:Array = [];
			var vector:Vector.<Number>;
			
			for(pts in actionArray)
			{
				vector = getPoint(frame,pts);
				
				m.transformVectors(vector, vector);
				current[pts] = vector;
			}
			//render joints
			for(pts in current)
			{
				vector = current[pts];
				//draw circles as joints when rendering wire
				if(pts!=HEAD+actionPointsNumber)
				{
					character.graphics.drawCircle(vector[0]/2,vector[1]/2,5);
				}
			}
			//render lines
			for(ln in lines)
			{
				vector = current[lines[ln][0]];
				character.graphics.moveTo(vector[0]/2,vector[1]/2);
				
				vector = current[lines[ln][1]];
				character.graphics.lineTo(vector[0]/2,vector[1]/2);
			}
		}			
		//get an interpolated point
		private function getPoint(frame:Number,pt:Number):Vector.<Number>
		{
			var p:Vector.<Number>=new Vector.<Number>(3);
			
			var fac1:Number=frame-int(frame);
			var fac2:Number=1-fac1;
			var f1:Number=int(frame) % actionFrameNumber;
			var f2:Number=int(frame+1) % actionFrameNumber;	
			var v1:Array=actionArray[pt][f1];
			var v2:Array=actionArray[pt][f2];
			p[0]=v1[0]*fac2+v2[0]*fac1;
			p[1]=v1[1]*fac2+v2[1]*fac1;
			p[2]=v1[2]*fac2+v2[2]*fac1;
			
			return p;
		}		
	}
}
